package com.ruoyi.common.security.service;


import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.api.MiniAppApi;
import com.ruoyi.common.api.MiniAppUserApi;
import com.ruoyi.common.core.domain.miniapp.MiniApp;
import com.ruoyi.common.core.domain.miniapp.MiniAppUser;
import com.ruoyi.common.core.domain.miniapp.WxLoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.MiniAppException;
import com.ruoyi.common.security.context.AuthenticationContextHolder;
import com.ruoyi.common.security.manager.WxMaServiceManager;
import com.ruoyi.common.security.miniapp.MiniAppByOpenIdAuthenticationToken;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;

@Slf4j
@Service
public class MiniAppLoginService
{
    @Autowired
    private MiniAppApi miniAppService;

    @Autowired
    private MiniAppUserApi miniAppUserService;

    @Autowired
    private WxMaServiceManager wxMaServiceManager;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenService tokenService;

    public JSONObject wxMiniLogin(WxLoginBody wxLoginBody) throws MiniAppException
    {
        JSONObject data = new JSONObject();
        MiniApp miniApp = miniAppService.selectMiniAppByAppid(wxLoginBody.getAppid());
        if(ObjectUtils.isEmpty(miniApp))
        {
            throw new MiniAppException("appid error");
        }
        WxMaService wxMaService = wxMaServiceManager.getWxMaService(wxLoginBody.getAppid());
        // 小程序用户验证
        Authentication authentication = null;
        try
        {
            WxMaJscode2SessionResult sessionResult = wxMaService.jsCode2SessionInfo(wxLoginBody.getCode());
            if(insertMiniAppUser(sessionResult,miniApp.getId(),wxLoginBody)){
                MiniAppByOpenIdAuthenticationToken miniAppByOpenIdAuthenticationToken = new MiniAppByOpenIdAuthenticationToken(sessionResult.getOpenid() + "," +  miniApp.getId());
                AuthenticationContextHolder.setContext(miniAppByOpenIdAuthenticationToken);
                authentication = authenticationManager.authenticate(miniAppByOpenIdAuthenticationToken);
            }else {
                throw new MiniAppException("用户认证失败！");
            }
        }
        catch (Exception e)
        {
            log.error("wxMiniLogin fail",e);
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String token = tokenService.createToken(loginUser);
        data.put("token", token);
        return data;
    }

    /**
     * 新增小程序用户
     * 如果查询不到openId,则新增
     * @param sessionResult 微信小程序用户信息
     * @param miniAppId 小程序ID
     * @param wxLoginBody 微信登录信息
     * @return 结果
     */
    private boolean insertMiniAppUser(WxMaJscode2SessionResult sessionResult,Long miniAppId,WxLoginBody wxLoginBody)
    {
        boolean saveUser=false;
        // 根据openId,miniAppId查询用户信息
        MiniAppUser miniAppUser = miniAppUserService.selectMiniAppUserByOpenIdAndMiniAppId(sessionResult.getOpenid(),miniAppId);
        if(!ObjectUtils.isEmpty(miniAppUser)) {
            log.error("用户已经存在,无需重复添加,openId:{},miniAppId:{}", sessionResult.getOpenid(), miniAppId);
            saveUser=true;
        }else{
            try {
                if(!ObjectUtils.isEmpty(wxLoginBody.getPhoneNumber())) {
                    JSONObject result = decrypt(
                            wxLoginBody.getPhoneNumber().getString("encryptedData"),
                            sessionResult.getSessionKey(),
                            wxLoginBody.getPhoneNumber().getString("iv")
                    );
                    String resultPhone=result.getString("phoneNumber");
                    if(StringUtils.isNotEmpty(resultPhone)&&StringUtils.isNotNull(resultPhone)){
                        miniAppUser = new MiniAppUser();
                        miniAppUser.setMiniAppId(miniAppId);
                        miniAppUser.setOpenId(sessionResult.getOpenid());
                        miniAppUser.setNickName("微信用户" + sessionResult.getOpenid().substring(sessionResult.getOpenid().length()-3));
                        //miniAppUser.setNickName(wxLoginBody.getNickName());
                        //miniAppUser.setAvatar(wxLoginBody.getAvatar());

                        // 返回手机号
                        miniAppUser.setTel(result.getString("phoneNumber"));
                        miniAppUser.setWeight(BigDecimal.valueOf(0.00));
                        miniAppUser.setHeight(0L);
                    }

                    int saveResult=miniAppUserService.insertMiniAppUser(miniAppUser);
                    if(saveResult>0){
                        saveUser=true;
                    }

                }
            }catch (Exception e) {
                log.error("手机号码解密失败", e);
            }
        }
        return saveUser;
    }



    /**
     * 解密微信小程序手机号
     * @param encryptedData 加密数据
     * @param sessionKey    会话密钥
     * @param iv            加密向量
     * @return 解密后的 JSON 对象
     * @throws Exception 解密异常
     */
    public static JSONObject decrypt(String encryptedData, String sessionKey, String iv) throws Exception {
        // 1. Base64 解码
        byte[] encryptedDataBytes = Base64.decodeBase64(encryptedData);
        byte[] sessionKeyBytes = Base64.decodeBase64(sessionKey);
        byte[] ivBytes = Base64.decodeBase64(iv);

        // 2. AES-128-CBC 解密
        SecretKeySpec secretKeySpec = new SecretKeySpec(sessionKeyBytes, "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        byte[] decryptedBytes = cipher.doFinal(encryptedDataBytes);

        // 3. 转换为 JSON
        String decryptedStr = new String(decryptedBytes, "UTF-8");
        return JSONObject.parseObject(decryptedStr);
    }

}
